﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Telerik.WinControls;
using Microsoft.Xrm.Sdk;
using CrmCodeGenerator.Core;
using Telerik.WinControls.UI;

namespace CrmCodeGenerator.UI
{
    public partial class App : Telerik.WinControls.UI.RadForm
    {
        public App()
        {
            InitializeComponent();
        }

        private void App_Load(object sender, EventArgs e)
        {
            this.radWizard1.HelpButton.Visibility = ElementVisibility.Hidden;
            this.radWizard1.CancelButton.Visibility = ElementVisibility.Hidden;
        }

        private void radWizard1_Next(object sender, Telerik.WinControls.UI.WizardCancelEventArgs e)
        {
            if (this.radWizard1.SelectedPage == this.radWizard1.Pages[0])
            {
                UpdateStatus(this.connectionstatus, "Connecting...");

                ConnectToCrm();

                e.Cancel = true; // don't move ahead, once the connection is done it'll auto move to next page
            }
            else if (this.radWizard1.SelectedPage == this.radWizard1.Pages[1])
            {
                StartGeneratingCode();
            }
        }

        private void radWizard1_Previous(object sender, WizardCancelEventArgs e)
        {
            if (this.radWizard1.SelectedPage == this.radWizard1.Pages[2])
            {
                ToggleControlVisibility(this.radWizard1.FinishButton, false);
            }
        }

        private void radWizard1_Finish(object sender, EventArgs e)
        {
            this.Close();
        }

        #region step 1 - connect to crm
        public IOrganizationService Connection { get; set; }

        private void ConnectToCrm()
        {
            GetCrmConnectionHandler gcch = new GetCrmConnectionHandler(BeginGetCrmConnection);
            AsyncCallback callback = new AsyncCallback(EndGetCrmConnection);

            gcch.BeginInvoke(this.discourl.Text, this.uniqueorgname.Text, this.domain.Text, this.username.Text, this.password.Text, callback, gcch);
        }

        private delegate IOrganizationService GetCrmConnectionHandler(string url, string org, string domain, string username, string password);

        private IOrganizationService BeginGetCrmConnection(string url, string org, string domain, string username, string password)
        {
            IOrganizationService sdk = null;

            try
            {
                sdk = QuickConnection.Connect(url, domain, username, password, org);
            }
            catch (Exception ex)
            {
                UpdateStatus(this.connectionstatus, string.Format("Connection failed: {0}", ex.Message));
            }

            return sdk;
        }

        private void EndGetCrmConnection(IAsyncResult result)
        {
            GetCrmConnectionHandler gcch = result.AsyncState as GetCrmConnectionHandler;
            IOrganizationService sdk = gcch.EndInvoke(result);

            //ToggleControl(this.connect, true);

            if (sdk != null)
            {
                this.Connection = sdk;

                UpdateStatus(this.connectionstatus, "Connected");

                SelectPage(1);
            }
        }
        #endregion

        #region step 2 - code generation settings
        private void t4browse_Click(object sender, EventArgs e)
        {
            var ofd = new OpenFileDialog { Filter = "T4 (*.tt)|*.tt" };
            if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                this.t4path.Text = ofd.FileName;
            }
        }

        private void outputbrowse_Click(object sender, EventArgs e)
        {
            var ofd = new OpenFileDialog { CheckFileExists = false };
            if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                this.outputpath.Text = ofd.FileName;
            }
        }
        #endregion

        #region step 3 - progress
        private delegate void GenerateCodeHandler(Mapper mapper);

        private void StartGeneratingCode()
        {
            UpdateStatus(this.statusmsg, "Generating code, please wait...");

            ToggleControlVisibility(this.ninja, true);
            ToggleControlVisibility(this.radWizard1.BackButton, false);
            ToggleControlVisibility(this.radWizard1.FinishButton, false);

            Mapper mapper = new Mapper
            {
                Settings = new Core.Model.Settings
                {
                    CrmConnection = this.Connection,

                    T4Path = this.t4path.Text,
                    OutputPath = this.outputpath.Text,

                    EntitiesToInclude = (this.include.Text ?? "").Split(','),
                    EntitiesToExclude = (this.exclude.Text ?? "").Split(','),

                    Context = new Core.Model.Context
                    {
                        Namespace = this.ns.Text,
                    }
                }
            };

            mapper.Message += new MapperHandler(mapper_Message);

            GenerateCodeHandler gch = new GenerateCodeHandler(BeginCodeGeneration);
            gch.BeginInvoke(mapper, new AsyncCallback(EndCodeGeneration), gch);
        }

        private void BeginCodeGeneration(Mapper mapper)
        {
            mapper.MapEntities();
        }

        private void EndCodeGeneration(IAsyncResult result)
        {
            GenerateCodeHandler gch = result.AsyncState as GenerateCodeHandler;
            gch.EndInvoke(result);

            ToggleControlVisibility(this.ninja, false);
            ToggleControlVisibility(this.radWizard1.BackButton, true);
            ToggleControlVisibility(this.radWizard1.FinishButton, true);
        }

        private void mapper_Message(object sender, MapperEventArgs e)
        {
            UpdateStatus(this.statusmsg, e.Message);
        }
        #endregion

        #region multi-threading stuff
        private void SelectPage(int pageNumber)
        {
            if (this.radWizard1.InvokeRequired)
            {
                this.radWizard1.BeginInvoke(new MethodInvoker(delegate()
                {
                    this.radWizard1.SelectedPage = this.radWizard1.Pages[pageNumber];
                }));
            }
            else { this.radWizard1.SelectedPage = this.radWizard1.Pages[pageNumber]; }
        }

        private void UpdateStatus(Control c, string msg)
        {
            if (c.InvokeRequired)
            {
                c.BeginInvoke(new MethodInvoker(delegate()
                {
                    c.Text = msg;
                }));
            }
            else { c.Text = msg; }
        }

        private void ToggleControl(Control c, bool enabled)
        {
            if (c.InvokeRequired)
            {
                c.BeginInvoke(new MethodInvoker(delegate()
                {
                    c.Enabled = enabled;
                }));
            }
            else { c.Enabled = enabled; }
        }

        private void ToggleControlVisibility(RadElement c, bool visible)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new MethodInvoker(delegate()
                {
                    c.Visibility = visible ? ElementVisibility.Visible : ElementVisibility.Hidden;
                }));
            }
            else { c.Visibility = visible ? ElementVisibility.Visible : ElementVisibility.Hidden; }
        }

        private void ToggleControlVisibility(Control c, bool visible)
        {
            if (c.InvokeRequired)
            {
                c.BeginInvoke(new MethodInvoker(delegate()
                {
                    c.Visible = visible;
                }));
            }
            else { c.Visible = visible; }
        }
        #endregion
    }
}
